package ch.ethz.fcl.metrobuzz.data.interchange;

import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import ch.ethz.fcl.metrobuzz.algorithm.list.ListOperation;
import ch.ethz.fcl.metrobuzz.data.MBData;
import ch.ethz.fcl.metrobuzz.data.scene.basemap.Edge;
import ch.ethz.fcl.metrobuzz.data.scene.basemap.Node;
import ch.ethz.fcl.metrobuzz.data.singapore.SGInterchange;
import ch.ethz.fcl.metrobuzz.spatialindex.object2d.Point2D;
import ch.ethz.fcl.metrobuzz.spatialindex.object2d.Polygon;

/**
 * write region information and interchange to files
 * 
 * @author Zeng Wei
 * @email zeng@arch.ethz.ch
 * 
 */
public class RegionWriter {
	private MBData data;
	private String distributionFile;
	private int level;

	public RegionWriter(MBData data) {
		this.data = data;
	}

	public void setDistributionFile(String file) {
		this.distributionFile = file;
	}

	public void setLevel(int level) {
		this.level = level;
	}

	public ArrayList<Region> createRegions() {
		ArrayList<Region> regions = new ArrayList<Region>();

		ArrayList<Polygon> zones = data.getSGInfo().getSGZones()
				.getZones(level);

		for (int i = 0; i < zones.size(); i++) {
			Polygon zone = zones.get(i);
			Region r = new Region(regions.size());

			for (Edge edge : data.getRoadNetwork().getPassingEdges()) {
				Node start = data.getScene().getNode(edge.getStartNodeIndex());
				if (zone.contains(new Point2D(start.getX(), start.getY()))) {
					r.addEdge(edge);
				}
			}
			r.setZoneIndex(data, i, level);
			regions.add(r);
		}

		float minDist = Float.MAX_VALUE;
		for (Region r1 : regions)
			for (Region r2 : regions) {
				if (r1.getIndex() != r2.getIndex() && r1.edges != null
						&& r2.edges != null) {
					float dist = r1.circosCentroid.distance(r2.circosCentroid);
					if (dist < minDist)
						minDist = dist;
				}
			}

		return regions;
	}

	public void calRegionDistributions(SGInterchange interchange) {
		for (Region region : interchange.getRegions(level))
			region.initIncomingOutgoing();

		for (Region region : interchange.getRegions(level)) {
			region.matrixSize = region.getConnectedInterchanges().size() + 1;

			if (region.totalDistribution == null)
				region.totalDistribution = new int[region.matrixSize][region.matrixSize][MBData.ONE_DAY_TIME];

			region.distribution = new int[region.matrixSize][region.matrixSize][MBData.ONE_DAY_TIME];
			for (int i = 0; i < region.matrixSize - 1; i++) {
				for (int j = 0; j < region.matrixSize - 1; j++)
					calRegionDistribution(region, i + 1, j + 1);
			}

			for (int i = 0; i < region.matrixSize - 1; i++) {
				calRegionDistribution(region, 0, i + 1);
				calRegionDistribution(region, i + 1, 0);
			}

		}
	}

	private void calRegionDistribution(Region region, int from, int to) {
		if (from == to)
			return;

		for (int startSlice = 0; startSlice < MBData.ONE_DAY_TIME; startSlice++) {
			List<Integer> thisIncomingTripIds = region.getIncomingTripIds(
					startSlice, startSlice + 1);
			List<Integer> thisExitingTripIds = region.getOutgoingTripIds(
					startSlice, startSlice + 1);
			Region startRegion;
			if (from != 0)
				startRegion = (Region) region.getConnectedInterchanges().get(
						from - 1);
			else
				startRegion = region;

			List<Integer> fromExitingTripIds = startRegion.getOutgoingTripIds(
					startSlice, startSlice + 2);
			List<Integer> fromToThis = ListOperation.intersection(
					fromExitingTripIds, thisIncomingTripIds);

			Region endRegion;
			if (to != 0)
				endRegion = (Region) region.getConnectedInterchanges().get(
						to - 1);
			else
				endRegion = region;
			List<Integer> toIncomingTripIds = endRegion.getIncomingTripIds(
					startSlice, startSlice + 2);

			List<Integer> thisToEnd = ListOperation.intersection(
					thisExitingTripIds, toIncomingTripIds);

			int common;
			if (from == 0) {
				int interchange = 0;
				for (int i = 0; i < region.matrixSize; i++)
					interchange += region.distribution[i][to][startSlice];
				common = thisToEnd.size() - interchange;
			} else if (to == 0) {
				int interchange = 0;
				for (int i = 0; i < region.matrixSize; i++)
					interchange += region.distribution[from][i][startSlice];
				common = fromToThis.size() - interchange;
			} else {
				List<Integer> commonTripIds = ListOperation.intersection(
						fromToThis, thisToEnd);
				common = commonTripIds.size();
			}
			region.distribution[from][to][startSlice] = common;
			region.totalDistribution[from][to][startSlice] += common;
		}
	}

	public void writeRegions(SGInterchange interchange, String file_name) {
		try {
			FileWriter fw = new FileWriter(file_name);
			for (Region region : interchange.getRegions(level)) {
				fw.write("index:\t" + region.index + "\t");
				fw.write("radius:\t" + region.circosRadius + "\t");
				fw.write("color:\t" + region.colorIndex + "\t");
				fw.write("zoneIndex:\t" + region.zoneIndex + "\t");
				fw.write("x:\t" + region.circosCentroid.getX() + "\t");
				fw.write("y:\t" + region.circosCentroid.getY() + "\n");
				fw.write("ConnectedInterchange:\t");
				if (region.getConnectedInterchanges() != null)
					for (Interchange i : region.getConnectedInterchanges())
						fw.write(i.index + "\t");
				fw.write("\n");

				fw.write("ColorIndex:\t");
				if (region.getConnectedInterchanges() != null)
					for (int i = 0; i < region.getConnectedInterchanges()
							.size(); i++)
						fw.write(region.arcElementColorMap.get(region
								.getConnectedInterchanges().get(i)) + "\t");
			}
			fw.write("\n\n");

			fw.flush();
			fw.close();

		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	public void writeRegionDistributions(SGInterchange interchange) {
		try {
			FileWriter fw = new FileWriter(distributionFile);
			for (Region region : interchange.getRegions(level)) {
				fw.write("index:\t" + region.index);
				fw.write("\n");
				for (int i = 0; i < region.matrixSize - 1; i++) {
					for (int j = 0; j < region.matrixSize - 1; j++)
						writeRegionDistribution(fw, region, i + 1, j + 1);
				}

				for (int i = 0; i < region.matrixSize - 1; i++) {
					writeRegionDistribution(fw, region, 0, i + 1);
					writeRegionDistribution(fw, region, i + 1, 0);
				}
			}

			fw.flush();
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void writeRegionDistribution(FileWriter fw, Region region,
			int from, int to) throws IOException {
		if (from == to || region.totalDistribution == null)
			return;
		fw.write(from + ":" + to + "\t");

		for (int startSlice = 0; startSlice < MBData.ONE_DAY_TIME; startSlice++) {
			fw.write(startSlice + ":"
					+ region.totalDistribution[from][to][startSlice] + "\t");
		}

		fw.write("\n");
	}

	public String getDistributionFile() {
		return distributionFile;
	}

	public int getLevel() {
		return level;
	}
}
